package com.hxht.storage.server.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.fxdigital.nvr.FindCondition;
import com.fxdigital.nvr.FindResult;
import com.fxdigital.nvr.IPChannelInfo;
import com.hxht.storage.server.bean.PlanRecordBean;
import com.hxht.storage.server.bean.RecordMarkBean;
import com.hxht.storage.server.bean.RecordSearchBean;
import com.hxht.storage.server.bean.RecordSearchDeviceInfoBean;
import com.hxht.storage.server.bean.RecordSearchResultBean;
import com.hxht.storage.server.bean.RecordStopBean;
import com.hxht.storage.server.bean.TimeIntersection;
import com.hxht.storage.server.dao.DayTemplateDao;
import com.hxht.storage.server.dao.DeviceChannelDao;
import com.hxht.storage.server.dao.NvrDeviceDao;
import com.hxht.storage.server.dao.PlanRecordDao;
import com.hxht.storage.server.dao.TimePeriodDao;
import com.hxht.storage.server.dao.WeekTemplateDao;
import com.hxht.storage.server.pojo.NvrDeviceInfo;
import com.hxht.storage.server.pojo.NvrIpchannelInfo;
import com.hxht.storage.server.pojo.PlanRecordInfo;
import com.hxht.storage.server.pojo.RecordMarkInfo;
import com.hxht.storage.server.pojo.VideoDeviceChannelInfo;
import com.hxht.storage.server.pojo.WeekTemplateInfo;
import com.hxht.storage.server.service.NvrDeviceService;
import com.hxht.storage.server.service.PlanRecordService;
import com.hxht.storage.server.util.NvrBeanTransUtil;

@Service
public class PlanRecordServiceImpl implements PlanRecordService{

	public static final String DEFAULT_TEMPLATE = "全天模板";
	
	private static final Logger LOG = Logger.getLogger(PlanRecordServiceImpl.class);
	
	@Autowired
	private NvrDeviceService nvrService;
	
	@Autowired
	private PlanRecordDao planRecordDao;
	
	@Autowired
	private NvrDeviceDao nvrDeviceDao;
	
	@Autowired
	private TimePeriodDao timePeriodDao;
	
	@Autowired
	private DayTemplateDao dayTemplateDao;
	
	@Autowired
	private WeekTemplateDao templateDao;

	@Autowired
	private DeviceChannelDao deviceChannelDao;
	
	@Override
	@Transactional(value="nvmpJpaEM", propagation=Propagation.REQUIRED)
	public boolean startRecord(PlanRecordBean recordBean) {

		LOG.info("start planRecord is beginning---------------");
		LOG.info("---deviceId 	  : " + recordBean.getDeviceId() + "---");
		LOG.info("---channelIndex : " + recordBean.getDeviceChannelIndex() + "---");
		
		PlanRecordInfo record = transRecordBeanToRecord(recordBean);
		
		// 返回空 没有可用的 ip通道
		if (record == null){
			LOG.warn("no idle ipchannel!");
			return false;
		}
		
		// 查询该device的 channel， 是否已设置计划录像
		// 若正在录像，直接返回成功
		boolean isRecording = isRecording(recordBean.getDeviceId(),
				recordBean.getDeviceChannelIndex());
		
		// 如果已存在 该设备的计划录像，则更新计划录像的时段
		if (isRecording){
			// TO-DO
			NvrIpchannelInfo ipchannelInfo = getRecordingIpchannel(recordBean.getDeviceId(), 
					recordBean.getDeviceChannelIndex());
			
			// nvr 不在线，返回失败
			if (!ipchannelInfo.getNvrDevice().isOnline()){
				LOG.warn("nvr is not online!");
				return false;
			}
			
			nvrService.updatePlanRecord(ipchannelInfo.getNvrDevice().getDeviceId(),
					ipchannelInfo.getNvrChannel(), recordBean.getParam(), true);
			
			
			WeekTemplateInfo weekTemplate = templateDao.getTemplateByName(recordBean.getParam().getTemplateName());
			PlanRecordInfo recording = planRecordDao.getRecodingDevice(recordBean.getDeviceId(),
				recordBean.getDeviceChannelIndex());
			recording.setWeekTemplate(weekTemplate);
			
			LOG.info("update plan record !");
			LOG.info("deviceId = " + recordBean.getDeviceId() +
					" / deviceChannel = " + recordBean.getDeviceChannelIndex() + 
					" / templateName = " + recordBean.getParam().getTemplateName());
			return true;
		} else {

			// 调用dvrService去进行 dvr.dll的jni调用，让nvr进行手工录像
			// record 返回的录像数据
			record = nvrService.startPlanRecord(record);

			// record为 null,说明 jni 调用nvr进行手工录像失败
			if (record == null) {
				return false;
			}
			
			deviceChannelDao.insert(record.getVideoDeviceChannel());
			planRecordDao.insert(record);

			LOG.info("start planRecord is success---------------");
			LOG.info("record at nvr = " + record.getIpChannel().getNvrDevice().getIp() +
					" / ipchannel = " + record.getIpChannel().getNvrChannel());
			return true;
		}
		
	}

	private PlanRecordInfo transRecordBeanToRecord(PlanRecordBean recordBean) {
		
		PlanRecordInfo record = new PlanRecordInfo();

		// 没有可用的ip通道，返回null
		String nvrIp = nvrService.hasIdleIpchannel();
		if (nvrIp == null) {
			return null;
		}
		
		VideoDeviceChannelInfo deviceChannelInfo = getDeviceChannelInfoFromRecordBean(recordBean);
		
//		// 生成计划录像的星期模板
//		Set<DayTemplateInfo> weekTemplate = getWeekTemplateFromRecordBean(recordBean);
//		record.setWeekTemplate(weekTemplate);
		
		// 获取该计划录像 所使用的计划模板
		WeekTemplateInfo template = templateDao.getTemplateByName(recordBean.getParam().getTemplateName());
		// 如果客户端传入的 计划模板名 不存在，则使用默认模板
		if (template == null){
			template = templateDao.getTemplateByName(DEFAULT_TEMPLATE);
		}
		
		record.setWeekTemplate(template);
		
		NvrIpchannelInfo ipchannel = new NvrIpchannelInfo();
		ipchannel.setNvrDevice(nvrDeviceDao.getNvrByIp(nvrIp));
		record.setIpChannel(ipchannel);
		IPChannelInfo info = transRecordChannelToNvrIpchannel(deviceChannelInfo);
		ipchannel.setProtocol(info.getProtocol());
		ipchannel.setStreamMode(info.getStreamMode());
		ipchannel.setIp(info.getIp());
		ipchannel.setPort(info.getPort());
		ipchannel.setUserName(info.getUserName());
		ipchannel.setPassword(info.getPassword());
		ipchannel.setDeviceChannel(info.getChannel());
		//设置该通道标示，正在被使用
		ipchannel.setIsIdle(false);
		
		record.setVideoDeviceChannel(deviceChannelInfo);
		
		record.setDeviceId(recordBean.getDeviceId());
		
		record.setDeviceChannel(recordBean.getDeviceChannelIndex());
		
		record.setIsRecoding(true);
		
		record.setUserId(recordBean.getUserId());
		
		record.setIsArchive(recordBean.getParam().isArchive());
		record.setIsAudioRecord(recordBean.getParam().isAudioRecord());
		record.setIsRedundancy(recordBean.getParam().isRedundancy());
		record.setLockDuration(recordBean.getParam().getLockDuration());
		record.setRecordDuration(recordBean.getParam().getRecordDuration());
		
		return record;
	
	}

	/**
	 * 将数据库中 录像相关的 ip设备通道相关信息，转化为 nvr绑定通道需要用到的IPChannelInfo
	 * @param videoDeviceChannelInfo
	 * @return
	 */
	private IPChannelInfo transRecordChannelToNvrIpchannel(VideoDeviceChannelInfo videoDeviceChannelInfo) {
		// TODO Auto-generated method stub
		IPChannelInfo ipchannel = new IPChannelInfo();
		
		ipchannel.setChannel(videoDeviceChannelInfo.getVideoServerChannelIndex());
		
		ipchannel.setIp(videoDeviceChannelInfo.getStreamUrl());
		if (videoDeviceChannelInfo.getStreamMode() == 1){
			ipchannel.setProtocol(0);
		} else {
			// 18,表示 ONVIF 协议
			ipchannel.setProtocol(18);
		}
		
		ipchannel.setPassword(videoDeviceChannelInfo.getPasswd());
		ipchannel.setPort(videoDeviceChannelInfo.getPort());
		
		ipchannel.setStreamMode(videoDeviceChannelInfo.getStreamMode());
		ipchannel.setUserName(videoDeviceChannelInfo.getUsername());
		
		return ipchannel;
	}

	/**
	 * 从 rtsp标准流地址中提取 ip地址的方法
	 * @param streamUrl 格式如   rtsp://admin:12345@192.168.1.121:554/2
	 * @return
	 */
	private String getIpFromRtspUrl(String streamUrl) {
		// TODO Auto-generated method stub
//		Pattern pattern = Pattern.compile("://([\\d.]+)[:/]");
		Pattern pattern = Pattern.compile("://([\\w_]+:[\\w_]+@)?([\\d.]+)[:/]");
		Matcher matcher = pattern.matcher(streamUrl);
		matcher.find();
		String ip = matcher.group(2);
		return ip;
	}
	
	private VideoDeviceChannelInfo getDeviceChannelInfoFromRecordBean(
			PlanRecordBean recordBean) {
		// TODO Auto-generated method stub
		VideoDeviceChannelInfo deviceChannelInfo = new VideoDeviceChannelInfo();
		
		// 业务知识：当 deviceChannelIndex < 1000 时，表示使用的是 主码流
		//       当 deviceChannelIndex >= 1000 时，表示使用的是 子码流
		if (recordBean.getDeviceChannelIndex() < 1000){
			deviceChannelInfo.setTransMode(0);
		} else {
			deviceChannelInfo.setTransMode(1);
		}
		
		// 当deviceType 为250时，代表标准流，streamMode设置为2
		if (recordBean.getDeviceType() == 250){
			deviceChannelInfo.setStreamMode(2);
		} else { 
			deviceChannelInfo.setStreamMode(1);
		}
		
		deviceChannelInfo.setProtocol(recordBean.getNetLinkMode());
		
		deviceChannelInfo.setStreamUrl(recordBean.getVideoServerIp());
		
		deviceChannelInfo.setVideoServerChannelIndex(recordBean.getVideoServerChannel());
		
		deviceChannelInfo.setUsername(recordBean.getUsername());
		
		deviceChannelInfo.setPasswd(recordBean.getPasswd());
		
		deviceChannelInfo.setPort(recordBean.getPort());
		
		return deviceChannelInfo;
	}

	@Override
	@Transactional(value="nvmpJpaEM", propagation=Propagation.REQUIRED)
	public boolean stopRecord(RecordStopBean recordStopBean) {
		
		LOG.info("user request to stop plan record:");
		LOG.info("deviceId = " + recordStopBean.getVideoDeviceId() + 
				" / channel = " + recordStopBean.getVideoDeviceChannelIndex());
		
		PlanRecordInfo recording = planRecordDao.getRecodingDevice(
				recordStopBean.getVideoDeviceId(),
				recordStopBean.getVideoDeviceChannelIndex());

		if (recording == null) {
			LOG.warn("the device is not recording now!");
			LOG.warn("deviceId = " + recordStopBean.getVideoDeviceId() + 
					" / channel = " + recordStopBean.getVideoDeviceChannelIndex());
			return false;
		}

		boolean stopResult = nvrService.stopPlanRecord(recording);
		if (!stopResult) {
			LOG.warn("stop plan record fail!");
			LOG.warn("deviceId = " + recordStopBean.getVideoDeviceId() + 
					" / channel = " + recordStopBean.getVideoDeviceChannelIndex());
			return false;
		}
		
		LOG.info("stop plan record success!");

		planRecordDao.update(recording);
		return true;
	}

	@Override
	public List<RecordSearchResultBean> searchRecordByDevice(
			RecordSearchBean searchBean, List<Integer> typeList) {

		LOG.info("search plan record is beginning---------------");
		
		List<RecordSearchResultBean> searchResultList = new ArrayList<RecordSearchResultBean>();
		
		List<PlanRecordInfo> recordList = new ArrayList<PlanRecordInfo>();
		
		Date startTime = NvrBeanTransUtil.transTimeInfoToDate(searchBean.getStartTime());
		
		Date stopTime = NvrBeanTransUtil.transTimeInfoToDate(searchBean.getStopTime());
		
		for (RecordSearchDeviceInfoBean deviceInfo:searchBean.getDeviceInfoList()){
			
			List<PlanRecordInfo> tempList = planRecordDao.getRecordByDevice(
					deviceInfo.getVideoDeviceId(), deviceInfo.getVideoDeviceChannelIndex(),
					startTime, stopTime);
			
			// 要去得 录像时间段 和 搜索时间段的 时间交集
			for (PlanRecordInfo record:tempList){
				TimeIntersection recordTime = new TimeIntersection(record.getStartTime(),
							record.getStopTime());
				TimeIntersection searchTime = new TimeIntersection(startTime, stopTime);
				
				TimeIntersection realTime = recordTime.countIntersection(searchTime);
				
				if (realTime != null && realTime.isValid()){
					record.setStartTime(realTime.getStartTime());
					record.setStopTime(realTime.getStopTime());
				}
			}
			
			recordList.addAll(tempList);
		}

//		Set<String> ipchannels = getNvrIpchannelFromRecords(recordList);
		
		Map<String, RecordSearchResultBean> resultMap = new HashMap<String, RecordSearchResultBean>();
		
		// typeList,代表 所有需要查询的 录像类型 列表
		for (int type : typeList) {

			// 每一个ipchannel 表示 某一个nvr上的某一个ip通道,某一个deviceId的某一个deviceChannelIndex录像
			// 在该ip通道上 查询录像时间片段
			for (PlanRecordInfo record : recordList) {
				String nvrIp = record.getIpChannel().getNvrDevice().getIp();
				int channel = record.getIpChannel().getNvrChannel();
				String deviceId = record.getDeviceId();
				int deviceChannelIndex = record.getDeviceChannel();

				FindCondition findCondition = new FindCondition();
				findCondition.setChannel(channel);
				findCondition.setType(type);
				findCondition.setStartTime(NvrBeanTransUtil.transDateToTimeInfo(record.getStartTime()));
				findCondition.setStopTime(NvrBeanTransUtil.transDateToTimeInfo(record.getStopTime()));

				// 在该ip通道上 查询录像时间片段
				List<FindResult> findResultList = nvrService
						.searchRecordFileByDevice(nvrIp, findCondition);

				// 如果没有找到任何信息，则继续下一通道寻找
				if (findResultList == null || findResultList.size() == 0)
					continue;

				// 设置 nvr登陆相关信息
				NvrDeviceInfo nvr = nvrService.getNvrDevice(nvrIp);
				RecordSearchResultBean searchResultBean = resultMap.get(nvrIp + "_" + channel);
				if (searchResultBean == null){
					searchResultBean = new RecordSearchResultBean();
					searchResultBean.setIp(nvrIp);
					searchResultBean.setIpchannel(Integer.valueOf(channel));
					searchResultBean.setPort(nvr.getPort());
					searchResultBean.setUsername(nvr.getUsername());
					searchResultBean.setPasswd(nvr.getPasswd());
					resultMap.put(nvrIp + "_" + channel, searchResultBean);
					searchResultList.add(searchResultBean);
				}

				for (FindResult findResult : findResultList) {

					Date start = NvrBeanTransUtil
							.transTimeInfoToDate(findResult.getStartTime());
					Date stop = NvrBeanTransUtil.transTimeInfoToDate(findResult
							.getStopTime());
					
					List<RecordMarkBean> markBeans = new ArrayList<RecordMarkBean>();
					
					for (RecordMarkInfo mark:record.getRecordMarks()){
						
						Date markTime = mark.getMarkTime();
						
						// 如果 markTime 是在 start 和 stop之间，则将该标记分配给录像时间片段
						if (!markTime.before(start) && !markTime.after(stop)){
							markBeans.add(NvrBeanTransUtil.transMarkInfoToMarkBean(mark));
						}
					}

					searchResultBean.addTimePeriod(deviceId, deviceChannelIndex,
							start, stop, markBeans, record.getUuid());
				}
			}
		}
		
		LOG.info("search plan record is end---------------");
		return searchResultList;
	}



	/**
	 * 统计出 PlanRecordInfo List中，总共用了多少个ip通道<br/>
	 * 统计信息格式如下：192.168.1.144:23:9d1b0c4c0a1c43ca95c4eaa7e779e7da:1
	 * @param recordList
	 * @return
	 */
	private Set<String> getNvrIpchannelFromRecords(
			List<PlanRecordInfo> recordList) {
		
		Set<String> ipchannelList = new HashSet<String>();
		
		for (PlanRecordInfo record:recordList){
			String nvrIp = record.getIpChannel().getNvrDevice().getIp();
			int channelIndex = record.getIpChannel().getNvrChannel();
			String deviceId = record.getDeviceId();
			int deviceChannelIndex = record.getDeviceChannel();
			ipchannelList.add(nvrIp + ":" + channelIndex + ":" + deviceId + ":" + deviceChannelIndex);
		}
		
		return ipchannelList;
	}

	@Override
	public boolean isRecording(String deviceId, int channelIndex) {
		PlanRecordInfo recording = planRecordDao.getRecodingDevice(deviceId, channelIndex);
		
		if (recording == null){
			return false;
		} 
		
		return true;
	}

	@Override
	@Transactional(value="nvmpJpaEM", propagation=Propagation.REQUIRED)
	public void stopRecordingOfIpchannel(String nvrIp, int ipchannel) {

		PlanRecordInfo recording = planRecordDao.getRecodingByIpchannel(nvrIp, ipchannel);
		
		// 没有和 该ip通道关联的正在录像设备
		if (recording == null){
			return;
		}
		
		recording.setIsRecoding(false);
		recording.setStopTime(nvrService.getCurrentNvrTime(nvrIp));
		
		planRecordDao.update(recording);
	}

	@Override
	public NvrIpchannelInfo getRecordingIpchannel(String deviceId,
			int deviceChannelIndex) {

		PlanRecordInfo recording = planRecordDao.getRecodingDevice(deviceId, deviceChannelIndex);
		
		if (recording == null){
			return null;
		} else {
			return recording.getIpChannel();
		}
	}

	@Override
	public PlanRecordInfo getrecordByUuid(String recordUuid) {
		
		return planRecordDao.getRecordByUuid(recordUuid);
		
	}

	@Override
	public List<PlanRecordInfo> getRecordingDevice() {
		
		return planRecordDao.getRecodingDevice();
	}

}
